# -*- coding: utf-8 -*-

import os, locale
import gtk,pango
import gettext, sys

from subprocess import Popen
from threading import Thread
from version import application

#initializing gettext
gettext.bindtextdomain(application.name, '/usr/share/locale')
gettext.textdomain(application.name)
_ = gettext.gettext

## Only for debugging : False / True
DEBUG = False

cat_name = {'Accessories':['Utility','Accessories'],
            'Games':['Game'],
            'Graphics':['Graphics'],
            'Network':['Internet', 'Network'],
            'Office':['Office'],
            'Development':['Development'],
            'Other':['Other'],
            'Settings':['Settings', 'X-XFCE'],
            'Multimedia':['AudioVideo', 'Audio', 'Video', 'Multimedia'],
            'System':['System', 'Administration'],
            }

cat_icon = {'Accessories':'applications-utilities',
            'Games':'applications-games',
            'Graphics':'applications-graphics',
            'Network':'applications-internet',
            'Office':'applications-office',
            'Other':'applications-other',
            'Development':'applications-development',
            'Settings':'preferences-desktop',
            'Multimedia':'applications-multimedia',
            'System':'applications-system',
            }

def hex2rgb(color_hex):
    ## convert Hex color to RGB
    hexcolor = color_hex.strip()
    hexcolor = hexcolor[1:]
    if len(hexcolor) != 6:
        logINFO('Invalid hex color, use #RRGGBB format.')
        return (0.0, 0.0, 0.0)
    r, g, b = hexcolor[:2], hexcolor[2:4], hexcolor[4:]
    r, g, b = [int(n, 16) for n in (r, g, b)]
    return (float(r)/256, float(g)/256, float(b)/256)

# Get current locale with stripped .ENCODING part
def get_current_locale():
    cur_locale = locale.setlocale(locale.LC_MESSAGES, '')
    encoding_part = cur_locale.find(".")
    if (cur_locale.find("@") != -1) and (encoding_part != -1):
        #Locale seems like en_US.utf8@latin
        cur_locale = cur_locale[0:encoding_part]+cur_locale[cur_locale.find("@"):]
    if encoding_part != -1:
        #Locale seems like en_US.utf8
        cur_locale = cur_locale[0:encoding_part]
    #Locale now seems like en_US or en_US@latin
    return cur_locale

def get_desktop_locale_variants():
    locale = get_current_locale()
    locale_list = []
    if locale.find("@") != -1:
        #en_US@latin
        locale_list.append(locale)
        #en_US
        locale_list.append(locale[0:locale.find("@")])
        #en@latin
        locale_list.append(locale[0:locale.find("_")]+locale[locale.find("@"):])
    else:
        #en_US
        locale_list.append(locale)
    #en
    locale_list.append(locale[0:locale.find("_")])
    return locale_list

# Find the Name / Command / Icon from .desktop file
def info_desktop(file, hideList, term_cmd=None):
    logINFO('Info desktop file  ..')
    cmd, icon, name, name_locales, category, comment_locales,comment = None, None, None, {}, 'Other', {}, None
    terminal = False
    try:
        cfile = open(file,"r")

        for line in cfile:
            if 'NoDisplay=true' in line:
                icon, category = None, None
                break
            if 'OnlyShowIn=' in line:
                onlyShowInList = line.split('=')[1].strip('\n ').split(';')
                for item in onlyShowInList:
                    if item in hideList:
                        icon, category = None, None
                        break
            elif 'Terminal=true' in line:
                terminal = True
            elif '=' in line:
                words = line.split('=')

                if words[0].find('Name') != -1:
                    name_locales[words[0]] = words[1].replace('\n','')
                if words[0].find('Comment') != -1:
                    comment_locales[words[0]] = words[1].replace('\n','')
                if words[0] == 'Icon':
                    icon = words[1].replace('\n','')
                    if not icon[0] == '/':
                        icon = icon.split('.',1)[0]
                if words[0] == 'Exec':
                    cmd = ''.join(words[1:])
                    cmd = cmd.replace('\n','')
                    cmd = cmd.split('%')[0]
                if words[0] == 'Categories':
                    tab = words[1].replace('\n','')
                    tab = tab.split(';')

                    for cat in tab:
                        found = False
                        for c_name in cat_name:
                            if cat in cat_name[c_name]:
                                category = c_name
                                found = True
                                break
                        if found:
                            break

        # if command is 'console only', launch it with terminal ..
        if terminal:
            if term_cmd:
                cmd = "%s -e %s" % (term_cmd, cmd)
            else:
                cmd = "xterm -e %s" % cmd

        current_locales = get_desktop_locale_variants()
        name_variants = {'C':None}
        for name_locale in name_locales:
            if name_locale == 'Name':
                name_variants['C'] = name_locales[name_locale]
                continue
            if name_locale[5:-1] in current_locales:
                name_variants[name_locale[5:-1]] = name_locales[name_locale]

        for current_locale in current_locales:
            if current_locale in name_variants:
                name = name_variants[current_locale]
                break

        if name == None:
            name = name_variants['C']

        comment_variants = {'C':None}
        for comment_locale in comment_locales:
            if comment_locale == 'Comment':
                comment_variants['C'] = comment_locales[comment_locale]
                continue
            if comment_locale[8:-1] in current_locales:
                comment_variants[comment_locale[8:-1]] = comment_locales[comment_locale]

        for current_locale in current_locales:
            if current_locale in comment_variants:
                comment = comment_variants[current_locale]
                break

        if comment == None:
            comment = comment_variants['C']

        cfile.close()

    except:
        print("# Error : parsing %s" % file)
        import traceback
        traceback.print_exc(sys.stderr)

    return (cmd, icon, name, category, comment)

def parse_user_menu(config):
    logINFO('Parse user menu ..')
    cfg_file = os.getenv('HOME') + '/.config/snapfly/usermenu'

    if os.access(cfg_file, os.F_OK|os.R_OK):
        print('Found user menu ..')
        f = open(cfg_file,'r')
        for line in f:
            try:
                if line == '\n' or line.startswith('#'):
                    continue
                elif line.startswith('@'):
                    if line.count("##") > 0:
                        cat = line[1:].strip('\n ').split("##")
                        config[cat[0]] = []
                        cat_icon[cat[0]] = cat[1]
                        cat_index = cat[0]
                    else:
                        print("# Error reading line: %s, not enought parameters (must be at least 2)" % line)                        
                else:
                    if line.count("##") > 2:
                        #Parameters:#[0]CMD#[1]Icon#[2]Name#[3]--/--#[4]Desc
                        config_line = line.strip('\n ').split('##')
                        cmd_line = [config_line[0],config_line[1],config_line[2],"",config_line[3]]
                        config[cat_index].append(cmd_line)
                    else:
                        print("# Error reading line: %s, not enought parameters (must be at least 3)" % line)
            except:
                print("# Error in user config menu at line: %s" % line)
                import traceback
                traceback.print_exc(sys.stderr)
        f.close()
    return config

def parse_desktop_dir(term_cmd=None, hideList=[]):
    config = {}
    logINFO('scan dir and parse .desktop file ..')
    
    desktop_dir = []
    
    for dir in ('/usr/share/applications/', 
                "%s/.local/share/applications/" % os.getenv('HOME'),
                "/usr/local/share/applications/",
                "/usr/share/applications/kde4/",
                "/usr/local/share/applications/kde4/"):
        if os.path.exists(dir):
            desktop_dir.append(dir)


    for dir in desktop_dir:
        listdir = os.listdir(dir)

        for i in listdir:
            if '.desktop' in i:
                desktop_file = dir + i
                (cmd, icon, name, category, comment) = info_desktop(desktop_file, hideList, term_cmd)

                if category:
                    if not config.has_key(category):
                        config[category] = []
                    if icon:
                        config[category].append((cmd, icon, name, desktop_file, comment))
                    else:
                        config[category].append((cmd, None, name, desktop_file, comment))
                else:
                    pass
                
    return config

def logINFO(msg, from_mod=""):
    if DEBUG:
        print("[SnapFly.%s] %s" % (from_mod, msg))

def launch_command(cmd):
    if cmd != ' ' and cmd is not None and cmd:
        os.chdir(os.environ['HOME'])
        logINFO("Exec. | %s |" % cmd)
        Popen(cmd, shell=True).pid
        realpath = os.path.dirname(os.path.realpath( __file__ ))
        os.chdir(realpath + '/..')

def pixbuf_from_file(file, width=None, height=None):
    pixbuf = None

    if file != None or file == '':
        if os.path.isfile(file):
            try:
                if not width and not height:
                    pixbuf = gtk.gdk.pixbuf_new_from_file(file)
                else:
                    width, height = int(width), int(height)
                    pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(file, width, height)
            except:
                logINFO("is a image ? => %s" % file)
                pixbuf = None
        else:
            logINFO("is a image ? => %s" % file)

    return pixbuf

def image_button(label, image, size, desc, width):
    bt = gtk.Button()
    bt.set_relief(gtk.RELIEF_NONE)
    bt.set_border_width(0)
    bt.set_focus_on_click(False)
    bt.set_property('can-focus', False)
    if not label:
        label = "Unknown"
    if image == None:
        bt_image = gtk.Image()
        bt_image.set_from_icon_name('image-x-generic',24)
    elif type(image) == gtk.Image:
        bt_image = image
    elif type(image) == gtk.gdk.Pixbuf:
        bt_image = gtk.Image()
        bt_image.set_from_pixbuf(image)
    else:
        bt_image = gtk.Image()
        try:
            pixbuf = pixbuf_from_file(image, size, size)
            bt_image.set_from_pixbuf(pixbuf)
        except:
            bt_image.set_from_icon_name('image-x-generic',24)
        
    bt_image.show()

    if desc == None:
        desc = ""
    desc = desc.replace("&","&amp;")

    bt_label = gtk.Label()
    bt_label.set_use_markup(True)
    bt_label.set_markup("{0}\n<span color='#aaaaaa'>{1}</span>".format(label,desc))
    bt_label.set_ellipsize(pango.ELLIPSIZE_END)
    bt_label.set_alignment(0, 0.5)
    align = gtk.Alignment(1, 0, 0.9, 0.5)
    align.show()
    align.add(bt_label)

    box = gtk.HBox()
    box.set_border_width(2)
    box.pack_start(bt_image,False, False)
    box.pack_start(align)
    box.show()
    bt.add(box)
    bt.show_all()
    return bt

class MakeMenu(Thread):
    def __init__ (self, menu, notebook, callback_action, width, move_on_scroll_action, terminal=None, themes=None, hideList=[]):
        logINFO('MakeMenu ..')
        Thread.__init__(self)
        self.menu = menu
        self.notebook = notebook
        self.callback_action = callback_action
        self.move_on_scroll_action = move_on_scroll_action
        self.terminal = terminal
        self.themes = themes
        self.hideList = hideList
        self.width = width

    def run(self):
        logINFO('MakeMenu - run..')
        menu = parse_desktop_dir(term_cmd=self.terminal, hideList=self.hideList)
        menu = parse_user_menu(menu)

        cat_tmp = []

        for categoty in menu:
            cat_tmp.append(categoty)
        cat_tmp.sort()

        self.cat_visible = True

        for category in cat_tmp:
            bBox = gtk.VBox()
            bBox.show()
            bBox.set_spacing(1)
            bBox.set_border_width(1)


            tab_box = gtk.HBox(False, 4)
            tab_label = gtk.Label(_(category))
            tab_icon = gtk.Image()

            if cat_icon[category][0] == '/':
                tab_icon.set_from_pixbuf(gtk.gdk.pixbuf_new_from_file(cat_icon[category]).scale_simple(24,24,gtk.gdk.INTERP_BILINEAR))
            else:
                tab_icon.set_from_icon_name(cat_icon[category], 24)

            tab_box.pack_start(tab_icon, False)
            tab_box.pack_start(tab_label, False)

            # needed, otherwise even calling show_all on the notebook won't
            # make the hbox contents appear.
            tab_box.show_all()

            scrolled = gtk.ScrolledWindow()

            gtk.gdk.threads_enter()
            self.notebook.append_page(scrolled, tab_box)
            gtk.gdk.threads_leave()

            app_tmp = []
            for application in menu[category]:
                app_tmp.append(application)
            app_tmp.sort()
            for application in app_tmp:
                if application[1] and not application[1][0] == '/':
                    icon = gtk.Image()
                    icon.set_from_icon_name(application[1], 24)
                    button = image_button(application[2], icon, 24, application[4], self.width)
                else:
                    button = image_button(application[2], application[1], 24, application[4], self.width)
                button.set_tooltip_text(application[4])
                button.connect("button-release-event", self.callback_action, application[0])
                bBox.pack_start(button, False, False)

            scrolled.show()
            scrolled.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
            scrolled.add_with_viewport(bBox)

            scrolled.connect("motion_notify_event", self.move_on_scroll_action)

        return len(menu)
